/* Gauge - Performance Measurement Framework for Mobile Platforms
 * Copyright (C) 2013, Juha Hytonen <juha.a.hytonen@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gauge;

import java.util.ArrayList;
import java.util.List;

/**
 * Holds functionality for communication between the agent and the server.
 * 
 * A CommunicationManager is used to send messages created by the Protocol.
 * Internally Protocol uses LogEntrySerialzer and MeasurementSerializer
 * to handle log entries and measurements respectively.
 * 
 * @note Changing this class might break the communication link!
 * 
 * @see AbstractCommunicationManager
 * @see LogEntrySerializer
 * @see MeasurementSerializer
 */
public final class Protocol {

	// Do not use any regex reserved characters here.
	public static final char MsgSeparator = '\n';
	public static final char MsgPartSeparator = ';';
	public static final char PollCharacter = '\t';
	
	// A message is prefixed with one of these...
	private static final String StartBenchmarkMsg = "BGN";
	private static final String MeasurementMsg = "MEA";
	private static final String LogMsg = "LOG";
	private static final String EndBenchmarksMsg = "END";
	// ...followed by this and the actual message.
	private static final String MsgTypeSeparator = ":";
	// These are indexes in the message string for previous.
	private static final int TypeBeginPos = 0;
	private static final int TypeEndPos = 3;
	private static final int PayloadBeginPos = 4;
	
	public static List<String> GetMessages(String data) {
		List<String> messages = new ArrayList<String>();
		do {
			String msg = extractNextMessageFrom(data);
			messages.add(msg);			
		}
		while(hasMoreMessages(data));
		return messages;
	}
	
	private static boolean hasMoreMessages(String data) {
		return (data.indexOf(MsgSeparator) > 0);
	}
	
	private static String extractNextMessageFrom(String data) {
		int messageBoundary = data.indexOf(MsgSeparator);
		messageBoundary = (messageBoundary == -1 ? data.length() : messageBoundary);
		String msg = clean(data.substring(0, messageBoundary));
		data = data.substring(messageBoundary);
		return msg;
	}
	
	private static String clean(String msg) {
		String cleanedMsg = msg;
		cleanedMsg = cleanedMsg.replaceAll(String.valueOf(PollCharacter), "");
		cleanedMsg = cleanedMsg.replaceAll(String.valueOf(MsgSeparator), "");
		return cleanedMsg;
	}
	
	public static String CreateStartBenchmarkMsg(String benchmarkName) {		
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(StartBenchmarkMsg);
		stringBuilder.append(MsgTypeSeparator);
		stringBuilder.append(benchmarkName);
		return stringBuilder.toString();
	}
	
	public static String CreateMeasurementMsg(Measurement measurement) {
		MeasurementSerializer serializer = new MeasurementSerializer(measurement);
		String measurementString = serializer.GetAsString();
		
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(MeasurementMsg);
		stringBuilder.append(MsgTypeSeparator);
		stringBuilder.append(measurementString);
		
		return stringBuilder.toString();
	}
	
	public static String CreateLogMsg(LogEntry logEntry) {
		LogEntrySerializer serializer = new LogEntrySerializer(logEntry);
		String logEntryString = serializer.GetAsString();
		
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(LogMsg);
		stringBuilder.append(MsgTypeSeparator);
		stringBuilder.append(logEntryString);
		
		return stringBuilder.toString();
	}
	
	public static String CreateEndBenchmarksMsg() {
		return EndBenchmarksMsg;
	}
	
	public static boolean IsStartBenchmarkMsg(String msg) {
		String type = extractMsgType(msg);
		return type.equals(StartBenchmarkMsg);
	}
	
	public static boolean IsMeasurementMsg(String msg) {
		String type = extractMsgType(msg);
		return type.equals(MeasurementMsg);
	}
	
	public static boolean IsLogMsg(String msg) {
		String type = extractMsgType(msg);
		return type.equals(LogMsg);
	}
	
	public static boolean IsEndBenchmarksMsg(String msg) {
		String type = extractMsgType(msg);
		return type.equals(EndBenchmarksMsg);
	}
	
	private static String extractMsgType(String msg) {
		return msg.substring(TypeBeginPos, TypeEndPos);
	}
	
	public static String ParseBenchmarkNameFrom(String msg) {
		return getPayload(msg);
	}
	
	public static Measurement ParseMeasurementFrom(String msg) {
		String measurementString = getPayload(msg);
		MeasurementSerializer serializer = new MeasurementSerializer(measurementString);
		return serializer.GetAsMeasurement();
	}
	
	public static LogEntry ParseLogEntryFrom(String msg) {
		String logEntryString = getPayload(msg);
		LogEntrySerializer serializer = new LogEntrySerializer(logEntryString);
		return serializer.GetAsLogEntry();
	}
	
	private static String getPayload(String msg) {
		return msg.substring(PayloadBeginPos);
	}

}
